<script setup lang="ts" generic="T">
/** 使用示例：
  <div>list虚拟列表:</div>
  <MyInfiniteVirtualScroll
    :container-height="300"
    :items="allItems"
    :item-height="itemHeight"
    :grid-gap="8"
  >
    <template #default="option">
      <div :style="{ border: '1px solid #fff' }">
        {{ option.data.name }}
      </div>
    </template>
  </MyInfiniteVirtualScroll>
  <div>Grid虚拟列表:</div>
  <MyInfiniteVirtualScroll
    :container-height="300"
    :items="allItems"
    :item-height="itemHeight"
    :grid-size="3"
    :grid-gap="[8, 32]"
  >
    <template #default="option">
      <div :style="{ flex: 1, border: '1px solid #fff' }">
        {{ option.data.name }}
      </div>
    </template>
  </MyInfiniteVirtualScroll>

  interface MyItem {
    id: number
    name: string
  }

  const datas = []
  for (let index = 0; index < 999; index++) {
    datas.push({
      id: index,
      name: `name-${index}`
    })
  }

  const allItems = ref<MyItem[]>(datas)
 */
import type { UseVirtualListOptions } from '@vueuse/core'
import { chunk } from 'lodash-es'

export interface Props<T> {
  // 滚动容易得高度
  containerHeight: number | string
  // 原始数据
  items: T[]
  // 横向滚动时才设置 itemWidth和itemHeight只能设置一个
  itemWidth?: number | ((index: number) => number),
  // 纵向滚动时才设置 itemWidth和itemHeight只能设置一个
  itemHeight?: number, //  | ((index: number) => number)
  // 额外渲染的数量
  overscan?: number
  // 网格布局时的列数
  gridSize?: number
  // 网格布局时的行间距和列间距[x,y]
  gridGap?: number | number[]
  // 数据唯一标识
  keyFiled?: string
}

const props = withDefaults(defineProps<Props<T>>(), {
  itemWidth: 0,
  itemHeight: 0,
  overscan: 5,
  gridGap: 8,
  keyFiled: 'id'
})

const { items, itemWidth, itemHeight, overscan, gridSize, gridGap } = toRefs(props)

const allItems = computed(() => {
  if (gridSize.value) {
    // console.log(222222222, chunk(items.value, gridSize.value))
    return chunk(items.value, gridSize.value) as T[][]
  }
  return items.value
})

const options = computed<UseVirtualListOptions>(() => {
  if (itemWidth.value) {
    return {
      itemWidth: itemWidth.value,
      overscan: overscan.value
    }
  }
  return {
    itemHeight: itemHeight.value + verticalGap.value,
    overscan: overscan.value
  }
})

const horizontalGap = computed(() => {
  return Array.isArray(gridGap.value) ? gridGap.value[0] : gridGap.value
})

const verticalGap = computed(() => {
  return Array.isArray(gridGap.value) ? gridGap.value[1] : gridGap.value
})

const itemStyle = computed(() => {
  let style = {}
  if (verticalGap.value) {
    style = { marginBottom: `${verticalGap.value}px` }
  }
  if (itemWidth.value) {
    style = { ...style, width: `${itemWidth.value}px` }
  }
  if (itemHeight.value) {
    style = { ...style, height: `${itemHeight.value}px` }
  }
  return style
})

const virtualList = gridSize?.value 
  ? useVirtualList<T[]>(allItems as Ref<T[][]>, options.value)
  : useVirtualList<T>(allItems as Ref<T[]>, options.value)
const { list, containerProps, wrapperProps } = virtualList
</script>

<template>
  <div>
    <div v-bind="containerProps" :style="{ height: `${containerHeight}px` }">
      <div v-bind="wrapperProps">
        <div
          v-for="(item, itemIndex) in list"
          :key="item.index"
          :style="itemStyle"
        >
          <div
            v-if="Array.isArray(item.data)"
            class="grid-row"
            :style="{
              gap: `${horizontalGap}px`
            }"
          >
            <template
              v-for="(gridItem, gridItemIndex) in item.data"
              :key="gridItem[keyFiled]"
            >
              <slot
                v-bind="{ ...gridItem, data: gridItem }"
                :index="gridSize ? itemIndex * gridSize + gridItemIndex : gridItemIndex"
              />
            </template>
          </div>
          <slot v-else v-bind="{ ...item, data: item.data }" />
        </div>
      </div>
      <!-- 用于嵌入上拉加载更多组件ScrollLoadMore后的sentinel元素站位 -->
      <slot name="sentinel" />
    </div>
  </div>
</template>

<style scoped>
.grid-row {
  display: flex;
  height: 100%;

  /* .grid-item {
    flex: 1;
  } */
}
</style>